package org.springframework.context.support;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.event.ApplicationEventMulticaster;
import org.springframework.context.event.ContextCloseEvent;
import org.springframework.context.event.ContextRefreshEvent;
import org.springframework.context.event.SimpleApplicationEventMulticaster;
import org.springframework.core.DefaultResourceLoader;

import java.util.List;

public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {

    public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";

    private ApplicationEventMulticaster applicationEventMulticaster;

    @Override
    public void refresh() throws BeansException {

        refreshBeanFactory();

        ConfigurableListableBeanFactory beanFactory  = getBeanFactory();

        invokeBeanFactoryPostProcessors(beanFactory);

        getBeanFactory().addBeanPostProcessor(new ApplicationContextAwareProcessor(this));


//        initMessageSource();

        initApplicationEventMulticaster();

        registerApplicationListeners();

        registerBeanPostProcessors();

        beanFactory.preInstantiateSingletons();

        finishRefresh();
    }

    private void registerBeanPostProcessors() {
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        List<BeanPostProcessor> beanPostProcessors = beanFactory.getBeanOfType(BeanPostProcessor.class);
        for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
            beanFactory.addBeanPostProcessor(beanPostProcessor);
        }
    }

    private void registerApplicationListeners() {
        List<ApplicationListener> listeners = getBeanOfType(ApplicationListener.class);
        for (ApplicationListener listener : listeners) {
            this.applicationEventMulticaster.addApplicationListener(listener);
        }
    }

    private void initApplicationEventMulticaster() {
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        SimpleApplicationEventMulticaster multicaster = new SimpleApplicationEventMulticaster();
        beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, multicaster);
        this.applicationEventMulticaster = multicaster;
    }

    private void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {

    }

    protected abstract void refreshBeanFactory() throws BeansException;

    private void finishRefresh() {
        applicationEventMulticaster.multicastEvent(new ContextRefreshEvent(this));
    }

    @Override
    public Object getBean(String name) throws BeansException {
        return getBeanFactory().getBean(name);
    }

    @Override
    public Object getBean(String name, Object... args) throws BeansException {
        return getBeanFactory().getBean(name, args);
    }

    @Override
    public <T> T getBean(Class<T> requiredType) throws BeansException {
        return getBeanFactory().getBean(requiredType);
    }

    @Override
    public String[] getBeanDefinitionNames() {
        return getBeanFactory().getBeanDefinitionNames();
    }

    @Override
    public <T> List<T> getBeanOfType(Class<T> requiredType) throws BeansException {
        return getBeanFactory().getBeanOfType(requiredType);
    }

    @Override
    public void publishEvent(ApplicationEvent event) {
        applicationEventMulticaster.multicastEvent(event);
    }

    @Override
    public void close() throws BeansException {

        //todo
        getBeanFactory();
    }

    protected abstract ConfigurableListableBeanFactory getBeanFactory();
}
